home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Graphics / nxyplot / Source / PlotView.m < prev    next >
Text File  |  1994-02-01  |  56KB  |  1,728 lines

  1.  
  2. /* Generated by Interface Builder */
  3.  
  4. #import "defs.h"
  5. #import "PlotView.h"
  6. #import "Plot.h"
  7. #import <appkit/SavePanel.h>
  8. #import <appkit/color.h>
  9. #import <appkit/FormCell.h>
  10. #import <appkit/PrintPanel.h>
  11. #import <appkit/Pasteboard.h>
  12.  
  13. /* The following routines are in auxil.m: */
  14. extern void count_labels(int *, double *, double, double, double);
  15. extern void autoformat(double, double, double, int *);
  16. extern void handformat(float, char *, int *);
  17.  
  18. @implementation PlotView
  19.  
  20. - (BOOL) acceptsFirstMouse { return YES;}      /* grab that mouse down event! */
  21.  
  22. - provideMainTitleFont   { return newMainTitleFont;}
  23. - provideXTitleFont      { return newXTitleFont;}
  24. - provideYTitleFont      { return newYTitleFont;}
  25. - provideLegendTitleFont { return newLegendTitleFont;}
  26. - provideLegendFont      { return newLegendFont;}
  27. - provideTicLabelFont    { return newTicLabelFont;}
  28. - (NXPoint) provideLegendBoxOrigin { return legendbox.origin;}
  29. - (NXPoint) provideXTitleBoxOrigin { return xtitlebox.origin;}
  30. - (NXPoint) provideYTitleBoxOrigin { return ytitlebox.origin;}
  31. - (NXPoint) provideMainTitleBoxOrigin { return maintitlebox.origin;}
  32.  
  33. - provideWindowFrame:(NXRect *)windowframe
  34. {
  35.   return [[self window] getFrame:windowframe];
  36. }
  37.  
  38. /* All these method names that start with "force" would more naturally start
  39.  * with "set", but everything malfunctions then; apparently it's a bad idea
  40.  * to have methods whose names begin with "set".  Is it also a bad idea to
  41.  * have methods whose names begin with "init"?
  42.  */
  43. - forceWindowFrame:(NXRect *)windowframe
  44. {
  45.   [[self window] placeWindow:windowframe];
  46.   oldbounds = bounds;        /* have to do this right here so that the */
  47.   [self display];        /* title boxes are not moved around again */
  48.   return self;
  49. }
  50.  
  51. - forceLegendBoxOrigin: (NXPoint)point
  52. {
  53.   legendbox.origin = point;
  54.   return self;
  55. }
  56.  
  57. - forceXTitleBoxOrigin: (NXPoint)point
  58. {
  59.   xtitlebox.origin = point;
  60.   return self;
  61. }
  62.  
  63. - forceYTitleBoxOrigin: (NXPoint)point
  64. {
  65.   ytitlebox.origin = point;
  66.   return self;
  67. }
  68.  
  69. - forceMainTitleBoxOrigin: (NXPoint)point
  70. {
  71.   maintitlebox.origin = point;
  72.   return self;
  73. }
  74.  
  75. - forceMainTitleFont:(char *)fontname :(float)fontsize
  76. {
  77.   newMainTitleFont =
  78.     [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
  79.   return self;
  80. }
  81.  
  82. - forceXTitleFont:(char *)fontname :(float)fontsize;
  83. {
  84.   newXTitleFont =
  85.     [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
  86.   return self;
  87. }
  88.  
  89. - forceYTitleFont:(char *)fontname :(float)fontsize;
  90. {
  91.   newYTitleFont =
  92.     [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
  93.   return self;
  94. }
  95.  
  96. - forceLegendFont:(char *)fontname :(float)fontsize;
  97. {
  98.   newLegendFont =
  99.     [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
  100.   return self;
  101. }
  102.  
  103. - forceLegendTitleFont:(char *)fontname :(float)fontsize;
  104. {
  105.   newLegendTitleFont =
  106.     [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
  107.   return self;
  108. }
  109.  
  110. - forceTicLabelFont:(char *)fontname :(float)fontsize;
  111. {
  112.   newTicLabelFont =
  113.     [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
  114.   return self;
  115. }
  116.  
  117. - initFrame:(const NXRect *)frameRect
  118. {
  119.   [super initFrame:frameRect];
  120.   legendbox.origin.x = XOFFSET + 50.0; /* get the legendbox initialized here */
  121.   legendbox.origin.y = YOFFSET + 50.0;
  122.   xtitlebox.origin.x = XOFFSET/4.0
  123.                    + (bounds.size.width - DEFAULTAXISTITLEWIDTH)/2.0;
  124.   xtitlebox.origin.y = bounds.origin.y + 16.0;
  125.   xtitlebox.size.width  = DEFAULTAXISTITLEWIDTH;
  126.   xtitlebox.size.height = DEFAULTFONTSIZE;
  127.   ytitlebox.origin.x = bounds.origin.x + 36.0;
  128.   ytitlebox.origin.y = YOFFSET/4.0
  129.                    + (bounds.size.height - DEFAULTAXISTITLEWIDTH)/2.0;
  130.   ytitlebox.size.width  = DEFAULTFONTSIZE;
  131.   ytitlebox.size.height = DEFAULTAXISTITLEWIDTH;
  132.   maintitlebox.origin.x = XOFFSET/4.0
  133.                        + (bounds.size.width - DEFAULTMAINTITLEWIDTH)/2.0;
  134.   maintitlebox.origin.y = bounds.size.height - 24.0;
  135.   maintitlebox.size.width  = DEFAULTMAINTITLEWIDTH;
  136.   maintitlebox.size.height = 14.0;
  137.  
  138. /*
  139.  * With NS 3.0, we start handling the dragging code here (not in PlotDelegate).
  140.  * registerForDraggedTypes is a new method in the View class.
  141.  */
  142.   [self registerForDraggedTypes:&NXFilenamePboardType count:1];
  143.  
  144.   oldbounds = bounds;
  145.  
  146.   return self;
  147. }
  148.  
  149. - (NXCoord *)xdata:(int)n    /* we use this in drawing the legend curves */
  150. {
  151.   xlegend[0] = (legendbox.origin.x + 5.33333)/ppxunit;
  152. /* We used to have 5.0 instead of 5.33333, but that can give erroneous-looking
  153.  * (albeit correct) results when writing to the screen: the pixels that are
  154.  * turned on when drawing a filled circle, for example, are much different
  155.  * when the circle's center is precisely at a pixel than when the circle's center
  156.  * is not precisely at a pixel.  The results are much better when the center
  157.  * of the circle is at a half-pixel point, and so that's what we do.
  158.  */
  159.   xlegend[1] = xlegend[0] + 40.0/ppxunit;
  160.   /* curve fragment in legend is 40 pixels in length */
  161.   if (!drawingLegendLines) xlegend[0] = 0.5*(xlegend[0] + xlegend[1]);
  162.   return xlegend;
  163. }
  164.  
  165. - (NXCoord **)ydata:(int)n    /* we use this in drawing the legend curves */
  166. {
  167.   int ncurves = [plotParam nCurvesTotal];
  168.   int j;
  169.   float yloc, yhgt;
  170.   const char * curvetitle;
  171.  
  172.   if (newLegendFont) {
  173.     yhgt = [newLegendFont pointSize];
  174.   }
  175.   else {
  176.     yhgt = DEFAULTFONTSIZE;
  177.   }
  178.  
  179.   yloc = (legendbox.origin.y - 2.0)/ppyunit;
  180.  
  181.   ylegend = (NXCoord **)realloc((void *)ylegend, ncurves*sizeof(NXCoord *));
  182.   for (j = 0; j < ncurves; j++) {
  183.     *(ylegend+j) = (NXCoord *)NULL; /* this is necessary */
  184.     *(ylegend+j) = (NXCoord *)realloc((void *)*(ylegend+j), 2*sizeof(NXCoord));
  185.     /* check for no lines and no symbols */
  186.     if ( ([plotParam providelinestyle:j] == NOLINE)  &&
  187.     ([plotParam providesymbolstyle:j] == NOSYMBOL) ) continue;
  188.     /* check for empty curvetitle */
  189.     curvetitle = [legendForm stringValueAt:j];
  190.     if ((drawingLegendLines || drawingLegendSymbols) && curvetitle[0]=='\0')
  191.       continue;
  192.     yloc = yloc + (yhgt+0.33333)/ppyunit;
  193. /* We used to have just yhgt (instead of yhgt+0.33333); see comments in preceding
  194.  * routine for the reason to change it.
  195.  */
  196.   }
  197.  
  198.   for (j=0; j<ncurves; j++) {
  199.     *(*(ylegend+j)+0) = yloc;
  200.     *(*(ylegend+j)+1) = yloc;
  201.     /* check for no lines and no symbols */
  202.     if( ([plotParam providelinestyle:j] == NOLINE)  &&
  203.        ( [plotParam providesymbolstyle:j] == NOSYMBOL) )continue;
  204.     /* check for empty curvetitle */
  205.     curvetitle = [legendForm stringValueAt:j];
  206.     if ((drawingLegendLines || drawingLegendSymbols) && curvetitle[0]=='\0')
  207.       continue;
  208.     yloc -= yhgt/ppyunit;
  209.   }
  210.   return ylegend;
  211. }
  212.  
  213. - (BOOL)has_ebars:(int)n
  214. {
  215.   return NO;
  216. }
  217.  
  218. - (int)nPoints:(int)n
  219. {
  220.   if (drawingLegendLines)  return 2;
  221.   else return 1;
  222. }
  223.  
  224. - (int)nCurves:(int)n
  225. {
  226.   return [plotParam nCurvesTotal];
  227. }
  228.  
  229. - (int)nFiles
  230. {
  231.   return 1;
  232. }
  233.  
  234. - startPlot
  235. {
  236.   const char * xtitle = [titles stringValueAt:0];
  237.   const char * ytitle = [titles stringValueAt:1];
  238.   const char * maintitle = [titles stringValueAt:2];
  239.   float yhgt;
  240.   id mainTitleFont,axisTitleFont;
  241.  
  242.   NXSetColor([plotParam provideBackgroundColor]);
  243.   [self clear:self];
  244.   PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 :
  245.          [accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW :
  246.                                          LINE_WIDTH_IF_PRINTING_COLOR);
  247.   NXSetColor([plotParam provideTextColor]);
  248.  
  249.   /*  get new font from fontmanager */
  250.   if ([plotParam shouldChangeXTitleFont]) {
  251.     newXTitleFont = [theFontManager convertFont:[theFontManager selFont]];
  252.   }
  253.  
  254.   if (newXTitleFont) {
  255.     axisTitleFont = [Font newFont:[newXTitleFont name] 
  256.            size:[newXTitleFont pointSize]
  257.            style:[newXTitleFont style]
  258.            matrix:NX_IDENTITYMATRIX];
  259.     yhgt = [newXTitleFont pointSize];
  260.   }
  261.   else {
  262.     axisTitleFont =
  263.       [Font newFont:"Helvetica" size:DEFAULTFONTSIZE
  264.             style:0 matrix:NX_IDENTITYMATRIX];
  265.     yhgt = DEFAULTFONTSIZE;
  266.   }
  267.  
  268.   [axisTitleFont set];
  269.  
  270.   xtitlebox.size.width  = [axisTitleFont getWidthOf:xtitle];
  271.   xtitlebox.size.height = yhgt;
  272.   if (oldbounds.size.width != bounds.size.width
  273.       || oldbounds.size.height != bounds.size.height) {
  274. /* Couldn't figure out how to do this with windowDidResize or superviewSizeChanged,
  275.  * so this grubby idea (with the global oldbounds variable) is used instead.
  276.  */
  277.     xtitlebox.origin.x = (xtitlebox.origin.x + 0.5*xtitlebox.size.width)
  278.       *(bounds.size.width/oldbounds.size.width) - 0.5*xtitlebox.size.width;
  279. //    xtitlebox.origin.y = (xtitlebox.origin.y + 0.5*xtitlebox.size.height)
  280. //      *(bounds.size.height/oldbounds.size.height) - 0.5*xtitlebox.size.height;
  281.     legendbox.origin.x = (legendbox.origin.x + 0.5*legendbox.size.width)
  282.       *(bounds.size.width/oldbounds.size.width) - 0.5*legendbox.size.width;
  283.     legendbox.origin.y = (legendbox.origin.y + 0.5*legendbox.size.height)
  284.       *(bounds.size.height/oldbounds.size.height) - 0.5*legendbox.size.height;
  285.   }
  286.   PSmoveto(xtitlebox.origin.x, xtitlebox.origin.y);
  287.   PSshow((char *)xtitle);
  288.  
  289.   /*  get new font from fontmanager */
  290.   if ([plotParam shouldChangeYTitleFont]) {
  291.     newYTitleFont =
  292.       [theFontManager convertFont:[theFontManager selFont]];
  293.   }
  294.  
  295.   if (newYTitleFont) {
  296.     axisTitleFont = [Font newFont:[newYTitleFont name] 
  297.            size:[newYTitleFont pointSize]
  298.            style:[newYTitleFont style]
  299.            matrix:NX_IDENTITYMATRIX];
  300.     yhgt = [newYTitleFont pointSize];
  301.   }
  302.   else {
  303.     axisTitleFont =
  304.       [Font newFont:"Helvetica" size:DEFAULTFONTSIZE
  305.             style:0 matrix:NX_IDENTITYMATRIX];
  306.     yhgt = DEFAULTFONTSIZE;
  307.   }
  308.  
  309.   [axisTitleFont set];
  310.  
  311.   ytitlebox.size.width  = yhgt;
  312.   ytitlebox.size.height = [axisTitleFont getWidthOf:ytitle];
  313.   if (oldbounds.size.width != bounds.size.width
  314.       || oldbounds.size.height != bounds.size.height) {
  315. //    ytitlebox.origin.x = (ytitlebox.origin.x + 0.5*ytitlebox.size.height)
  316. //      *(bounds.size.width/oldbounds.size.width) - 0.5*ytitlebox.size.height;
  317.     ytitlebox.origin.y = (ytitlebox.origin.y + 0.5*ytitlebox.size.width)
  318.       *(bounds.size.height/oldbounds.size.height) - 0.5*ytitlebox.size.width;
  319.   }
  320.   PSmoveto(ytitlebox.origin.x + yhgt, ytitlebox.origin.y);
  321.   PSgsave();
  322.   PSrotate(90.0);
  323.   PSshow((char *)ytitle);
  324.   PSgrestore();
  325.   
  326.   if ([plotParam shouldChangeMainTitleFont]) {
  327.     newMainTitleFont =
  328.       [theFontManager convertFont:[theFontManager selFont]];
  329.   }
  330.  
  331.   if (newMainTitleFont) {
  332.     mainTitleFont = [Font newFont:[newMainTitleFont name] 
  333.            size:[newMainTitleFont pointSize]
  334.            style:[newMainTitleFont style]
  335.            matrix:NX_IDENTITYMATRIX];
  336.     yhgt = [newMainTitleFont pointSize];
  337.   }
  338.   else {
  339.     mainTitleFont =
  340.       [Font newFont:"Helvetica" size:14.0 style:0 matrix:NX_IDENTITYMATRIX];
  341.     yhgt = 14.0;
  342.   }
  343.  
  344.   [mainTitleFont set];
  345.  
  346.   maintitlebox.size.width  = [mainTitleFont getWidthOf:maintitle];
  347.   maintitlebox.size.height = yhgt;
  348.   if (oldbounds.size.width != bounds.size.width
  349.       || oldbounds.size.height != bounds.size.height) {
  350.     maintitlebox.origin.x = (maintitlebox.origin.x + 0.5*maintitlebox.size.width)
  351.       *(bounds.size.width/oldbounds.size.width) - 0.5*maintitlebox.size.width;
  352.     maintitlebox.origin.y = (maintitlebox.origin.y + 0.5*maintitlebox.size.height)
  353.       *(bounds.size.height/oldbounds.size.height) - 0.5*maintitlebox.size.height;
  354.     oldbounds = bounds;
  355.   }
  356.   PSmoveto(maintitlebox.origin.x, maintitlebox.origin.y);
  357.   PSshow((char *)maintitle);
  358.  
  359.   // box around the plot:
  360.   if ([borderBoxOnOff state]) {
  361.     PSsetlinewidth([borderBoxThicknessText floatValue]);
  362.     PSmoveto(0.0, 0.0);
  363.     PSlineto(bounds.size.width, 0.0);
  364.     PSlineto(bounds.size.width, bounds.size.height);
  365.     PSlineto(0.0, bounds.size.height);
  366.     PSlineto(0.0, 0.0);
  367.     PSstroke();
  368.     PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 :
  369.          [accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW :
  370.                                          LINE_WIDTH_IF_PRINTING_COLOR);
  371.   }
  372.   // The preceding doesn't work quite right: for some reason the vertical line
  373.   // on the right hand side of the plot doesn't show up on the display.  The
  374.   // line is there if you print or preview the file, however.
  375.   return self;
  376. }
  377.  
  378. - setDrawColor:(float) color
  379. {
  380.   PSsetgray(color);
  381.   return self;
  382. }
  383.  
  384. - drawLines:sender :(BOOL)xaxislog :(BOOL)yaxislog
  385. /*
  386.  * This is coded so that when drawLines is called with plotParam as argument,
  387.  * it draws the data curves; when drawLines is called with self (plotView) as
  388.  * argument, it draws the short line segments in the legend box.
  389.  */
  390. {
  391.   int i, j, jrun, n;
  392.   NXCoord *x;
  393.   NXCoord **y;
  394.   int npoints, ncurves;
  395.   int curveindex = 0;        /* cumulative index */
  396.   int linestyle;
  397.   float thick = [lineThicknessText floatValue];
  398.   float pattern0[] = {};    /* solid      */
  399.   float pattern1[] = {4.0, 4.0}; /* dash       */
  400.   float pattern2[] = {1.0, 3.0}; /* dot        */
  401.   float pattern3[] = {7.0, 3.0, 3.0, 3.0}; /* chain dash */
  402.   float pattern4[] = {7.0, 4.0, 1.0, 4.0}; /* chain dot  */
  403.   const char * curvetitle;
  404.  
  405.   for (n=0; n<[sender nFiles]; n++) { /* loop over all active files */
  406.     x  = [sender xdata:n];
  407.     y  = [sender ydata:n];
  408.     ncurves = [sender nCurves:n];
  409.     npoints = [sender nPoints:n];
  410.     PSnewpath();
  411.     PSsetlinewidth(NXDrawingStatus==NX_DRAWING? thick :
  412.      [accPrintColorButton state]==0? MAX(thick, LINE_WIDTH_IF_PRINTING_BW) :
  413.                                  MAX(thick, LINE_WIDTH_IF_PRINTING_COLOR));
  414.     for (jrun=curveindex; jrun<curveindex+ncurves; jrun++) {
  415.       linestyle = [plotParam providelinestyle:jrun];
  416.       NXSetColor([plotParam provideCurveColor:jrun]);
  417.       switch(linestyle) {
  418.       case SOLID:
  419.     PSsetdash(pattern0, 0, 0.0);
  420.     break;
  421.       case DASH:
  422.     PSsetdash(pattern1, 2, 0.0);
  423.     break;
  424.       case DOT:
  425.     PSsetdash(pattern2, 2, 0.0);
  426.     break;
  427.       case CHAINDASH:
  428.     PSsetdash(pattern3, 4, 0.0);
  429.     break;
  430.       case CHAINDOT:
  431.     PSsetdash(pattern4, 4, 0.0);
  432.     break;
  433.       case NOLINE:            /* no lines */
  434.     continue;
  435.       }
  436.       if (linestyle == NOLINE) continue; /* no lines, go to next curve */
  437.       /* If we're drawing the legend and there is no title, don't
  438.        * bother to draw the line:
  439.        */
  440.       curvetitle = [legendForm stringValueAt:jrun];
  441.       if (drawingLegendLines && curvetitle[0]=='\0') continue;
  442.       j = jrun - curveindex;    /* for indexing into the y array */
  443.       if (!xaxislog && !yaxislog) {
  444.     PSmoveto(x[0]*ppxunit, *(*(y + j)) * ppyunit );
  445.     for (i=1; i<npoints; i++) {
  446.       if ( (i % 512) == 0 ) {
  447.         PSstroke();
  448.         PSnewpath();
  449.         PSmoveto(x[i-1]*ppxunit, *(*(y+j)+i-1) * ppyunit);
  450.       }
  451.       PSlineto(x[i]*ppxunit, *(*(y+j)+i) * ppyunit);
  452.     }
  453.     PSstroke();
  454.       }
  455.       else if (!xaxislog && yaxislog) {
  456.     PSmoveto(x[0]*ppxunit, (float)log10((double)*(*(y + j))) * ppyunit );
  457.     for (i=1; i<npoints; i++) {
  458.       if ( (i % 512) == 0 ) {
  459.         PSstroke();
  460.         PSnewpath();
  461.         PSmoveto(x[i-1]*ppxunit, 
  462.              (float)log10((double)*(*(y+j)+i-1)) * ppyunit);
  463.       }
  464.       PSlineto(x[i]*ppxunit, (float)log10((double)*(*(y+j)+i)) * ppyunit);
  465.     }
  466.     PSstroke();
  467.       }
  468.       else if (xaxislog && !yaxislog) {
  469.     PSmoveto((float)log10((double)x[0])*ppxunit, *(*(y + j)) * ppyunit );
  470.     for (i=1; i<npoints; i++) {
  471.       if ( (i % 512) == 0 ) {
  472.         PSstroke();
  473.         PSnewpath();
  474.         PSmoveto((float)log10((double)x[i-1])*ppxunit,
  475.              *(*(y+j)+i-1) * ppyunit);
  476.       }
  477.       PSlineto((float)log10((double)x[i])*ppxunit, *(*(y+j)+i) * ppyunit);
  478.     }
  479.     PSstroke();
  480.       }
  481.       else if (xaxislog && yaxislog) {
  482.     PSmoveto((float)log10((double)x[0])*ppxunit,
  483.          (float)log10((double)*(*(y + j))) * ppyunit );
  484.     for (i=1; i<npoints; i++) {
  485.       if ( (i % 512) == 0 ) {
  486.         PSstroke();
  487.         PSnewpath();
  488.         PSmoveto((float)log10((double)x[i-1])*ppxunit,
  489.              (float)log10((double)*(*(y+j)+i-1)) * ppyunit);
  490.       }
  491.       PSlineto((float)log10((double)x[i])*ppxunit,
  492.            (float)log10((double)*(*(y+j)+i)) * ppyunit);
  493.     }
  494.     PSstroke();
  495.       }
  496.     }
  497.     curveindex += ncurves;
  498.   }
  499.   PSsetdash(pattern0, 0, 0.0);    /* back to solid lines  */
  500.   return self;
  501. }
  502.  
  503. /*
  504.  * Our first idea for drawing the symbols was: draw each symbol just once
  505.  * in an off-screen window, then composite them in where needed.  This worked
  506.  * fine for screen drawing (modulo a little difficulty in compositing to just
  507.  * the right spot), but was a miserable failure when it came to printing: the
  508.  * print machinery scaled the composite bitmap.  We finally decided to abandon
  509.  * compositing, in the interest of keeping our screen drawing code the same as
  510.  * our printing code.  Should check this again under version 2.0.
  511.  */
  512. - drawSymbols:sender :(BOOL)xaxislog :(BOOL)yaxislog
  513. /*
  514.  * This is coded so that when drawSymbols is called with plotParam as argument,
  515.  * it draws the data curves; when drawSymbols is called with self (plotView) as
  516.  * argument, it draws the symbols in the legend box.
  517.  */
  518. {
  519. /* Our current code in drawSymbols could perhaps be sped up by using wraps
  520.  * or DPSuserpath(). See if it's too slow as it stands before trying to
  521.  * speed it up.
  522.  */
  523.   int i, j, jrun, n;
  524.   float size = 4.0;
  525.   NXCoord *x;
  526.   NXCoord **y;
  527.   int npoints, ncurves;
  528.   int curveindex = 0;
  529.   int symbolstyle;
  530.   register float xtmp, ytmp;
  531.   const char * curvetitle;
  532.  
  533.   size = [symbolSizeText floatValue];
  534.  
  535.   PSgsave();
  536.   for (n=0; n<[sender nFiles]; n++) { /* loop over all active files */
  537.     x = [sender xdata:n];
  538.     y = [sender ydata:n];
  539.     ncurves = [sender nCurves:n];
  540.     npoints = [sender nPoints:n];
  541.     PSnewpath();
  542.     PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 :
  543.                  [accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW :
  544.                                          LINE_WIDTH_IF_PRINTING_COLOR);
  545.     for (jrun=curveindex; jrun<curveindex+ncurves; jrun++) {
  546.       symbolstyle = [plotParam providesymbolstyle:jrun];
  547.       NXSetColor([plotParam provideCurveColor:jrun]);
  548.       /* If we're drawing the legend and there is no title, don't
  549.        * bother to draw the symbol:
  550.        */
  551.       curvetitle = [legendForm stringValueAt:jrun];
  552.       if (drawingLegendSymbols && curvetitle[0]=='\0') continue;
  553.       j = jrun - curveindex;    /* for indexing into the y array */
  554.       switch(symbolstyle) {
  555.       case NOSYMBOL:
  556.     continue;
  557.     break;
  558.       case CIRCLE:
  559.     for (i=0; i<npoints; i++) {
  560.       /*      circle_at(x[i], *(*(y+j)+i));      */
  561.       xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
  562.       ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
  563.       PSarc(xtmp * ppxunit, ytmp * ppyunit, size, 0.0, 360.0);
  564.       PSfill();
  565.     }
  566.     break;
  567.       case XMARK:
  568.     for (i=0; i<npoints; i++) {
  569.       /*      x_at(x[i], *(*(y+j)+i));           */
  570.       xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
  571.       ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
  572.       PSmoveto(xtmp * ppxunit  - size, ytmp * ppyunit  - size);
  573.       PSrlineto(2.0*size, 2.0*size);
  574.       PSmoveto(xtmp * ppxunit  - size, ytmp * ppyunit  + size);
  575.       PSrlineto(2.0*size, -2.0*size);
  576.       PSstroke();
  577.     }
  578.     break;
  579.       case UPTRIANGLE:
  580.     for (i=0; i<npoints; i++) {
  581.       /*      uptriangle_at(x[i], *(*(y+j)+i));    */
  582.       xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
  583.       ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
  584.       PSmoveto(xtmp * ppxunit  - size, ytmp * ppyunit  - size/SQRT3);
  585.       PSrlineto(2.0*size, 0.0);
  586.       PSrlineto(-size, size*SQRT3);
  587.       PSclosepath();
  588.       PSfill();
  589.     }
  590.     break;
  591.       case DOWNTRIANGLE:
  592.     for (i=0; i<npoints; i++) {
  593.       /*      downtriangle_at(x[i], *(*(y+j)+i));     */
  594.       xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
  595.       ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
  596.       PSmoveto(xtmp * ppxunit  - size, ytmp * ppyunit  + size/SQRT3);
  597.       PSrlineto(2.0*size, 0.0);
  598.       PSrlineto(-size, -size*SQRT3);
  599.       PSclosepath();
  600.       PSfill();
  601.     }
  602.     break;
  603.       case DIAMOND:
  604.     for (i=0; i<npoints; i++) {
  605.       /*      diamond_at(x[i], *(*(y+j)+i));          */
  606.       /* 3 pixels horizontal, 4 pixels vertical */
  607.       xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
  608.       ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
  609.       PSmoveto(xtmp * ppxunit, ytmp * ppyunit  - size);
  610.       PSrlineto(3.0/4.0*size, size);    
  611.       PSrlineto(-3.0/4.0*size, size); /* gives a pleasing diamond shape */
  612.       PSrlineto(-3.0/4.0*size, -size);
  613.       PSclosepath();
  614.       PSfill();
  615.     }
  616.     break;
  617.       case SQUARE:
  618.     for (i=0; i<npoints; i++) {
  619.       /*      square_at(x[i], *(*(y+j)+i));            */
  620.       xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
  621.       ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
  622.       PSrectfill(xtmp * ppxunit  - size, ytmp * ppyunit  - size,
  623.              2.0*size, 2.0*size);
  624.     }
  625.     break;
  626.       case PLUS:
  627.     for (i=0; i<npoints; i++) {
  628.       /*      plus_at(x[i], *(*(y+j)+i));              */
  629.       xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
  630.       ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
  631.       PSmoveto(xtmp * ppxunit, ytmp * ppyunit  - size);
  632.       PSrlineto(0.0, 2.0*size);
  633.       PSmoveto(xtmp * ppxunit  - size, ytmp * ppyunit);
  634.       PSrlineto(2.0*size, 0.0);
  635.       PSstroke();
  636.     }
  637.     break;
  638.       }
  639.     }
  640.     curveindex += ncurves;
  641.   }
  642.   PSgrestore();
  643.  
  644.   return self;
  645. }
  646.  
  647. - drawErrorBars :(BOOL)xaxislog :(BOOL)yaxislog
  648. {
  649.   int     n, j, npoints, ncurves, jrun, i;
  650.   NXCoord *x, *ex = NULL;    /* ex and ey initialized to avoid */
  651.   NXCoord **y, **ey = NULL;    /* compiler warning               */
  652.   int     curveindex = 0;
  653.   register float xtmp, ytmp, tmp1, tmp2;
  654.   float   width = [errorBarBaseWidth floatValue];
  655.   BOOL    exbars, eybars;
  656.  
  657.   PSgsave();
  658.   for (n=0; n<[plotParam nFiles]; n++) { /* loop over all active files */
  659.     exbars = [plotParam has_exbars:n];
  660.     eybars = [plotParam has_eybars:n];
  661.     ncurves = [plotParam nCurves:n];
  662.     if (!exbars  && !eybars ) {       /* skip if no error bars */
  663.       curveindex += ncurves;          /* but have to bump this index, */
  664.       continue;                  /* to get the colors correct    */
  665.     }
  666.     x = [plotParam xdata:n];    /* we have to draw error bars */
  667.     if (exbars) {
  668.       ex = [plotParam exdata:n];
  669.     }
  670.     y = [plotParam ydata:n];
  671.     if (eybars) {
  672.       ey = [plotParam eydata:n];
  673.     }
  674.     npoints = [plotParam nPoints:n];
  675.     PSnewpath();
  676.     PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 :
  677.          [accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW :
  678.                                          LINE_WIDTH_IF_PRINTING_COLOR);
  679.     for (jrun=curveindex; jrun<curveindex+ncurves; jrun++) {
  680.       NXSetColor([plotParam provideCurveColor:jrun]);
  681.       j = jrun - curveindex;    /* for indexing into the y and ey arrays */
  682.       /* check if error bars are desired */
  683.       if (exbars &&  [[errorBarMatrix cellAt :n :0] state] == 1) {
  684.     for (i=0; i<npoints; i++) {
  685.       ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
  686.       if (xaxislog) {
  687.         if (x[i] - ex[i] <= 0.0) { /* avoid log of negative */
  688.           tmp1 = (float)log10( (double) x[i] ); /* what to do? */
  689.         }
  690.         else {
  691.           tmp1 = (float)log10( (double) (x[i] - ex[i]) );
  692.         }
  693.         if (x[i] + ex[i] <= 0.0) { /* again avoid log of negative */
  694.           tmp2 = (float)log10( (double) x[i] );
  695.         }
  696.         else {
  697.           tmp2 = (float)log10( (double) (x[i] + ex[i]) );
  698.         }
  699.       }
  700.       else {
  701.         tmp1 = x[i] - ex[i];
  702.         tmp2 = x[i] + ex[i];
  703.       }
  704.       PSmoveto(tmp1 * ppxunit, ytmp * ppyunit);
  705.       PSrlineto((tmp2-tmp1)*ppxunit, 0.0);
  706.       if (width > 0.0) {
  707.         PSmoveto(tmp1 * ppxunit, ytmp*ppyunit - width/2.0);
  708.         PSrlineto(0.0, width);
  709.         PSmoveto(tmp2 * ppxunit, ytmp*ppyunit - width/2.0);
  710.         PSrlineto(0.0, width);
  711.       }
  712.       PSstroke();
  713.     }
  714.       }
  715.       if (eybars && [[errorBarMatrix cellAt :n :j+1] state] == 1) {
  716.     for (i=0; i<npoints; i++) {
  717.       xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
  718.       if (yaxislog) {
  719.         if ((*(*(y+j)+i) - *(*(ey+j)+i)) <= 0.0) { /* avoid log of negative */
  720.           tmp1 = (float)log10( (double) (*(*(y+j)+i)) );
  721.         }
  722.         else {
  723.           tmp1 = (float)log10( (double) (*(*(y+j)+i) - *(*(ey+j)+i)) );
  724.         }
  725.         if ((*(*(y+j)+i) + *(*(ey+j)+i)) <= 0.0) { /* avoid log of negative */
  726.           tmp2 = (float)log10( (double) (*(*(y+j)+i)) );
  727.         }
  728.         else {
  729.           tmp2 = (float)log10( (double) (*(*(y+j)+i) + *(*(ey+j)+i)) );
  730.         }
  731.       }
  732.       else {
  733.         tmp1 = *(*(y+j)+i) - *(*(ey+j)+i);
  734.         tmp2 = *(*(y+j)+i) + *(*(ey+j)+i);
  735.       }
  736.       PSmoveto(xtmp * ppxunit, tmp1 * ppyunit);
  737.       PSrlineto(0.0, (tmp2-tmp1)*ppyunit);
  738.       if (width > 0.0) {
  739.         PSmoveto(xtmp*ppxunit - width/2.0, tmp1 * ppyunit);
  740.         PSrlineto(width, 0.0);
  741.         PSmoveto(xtmp*ppxunit - width/2.0, tmp2 * ppyunit);
  742.         PSrlineto(width, 0.0);
  743.       }
  744.       PSstroke();
  745.     }
  746.       }
  747.     }
  748.     curveindex += ncurves;
  749.   }
  750.   PSgrestore();
  751.  
  752.   return self;
  753. }
  754.  
  755.  
  756. - startLegend
  757. {
  758.   id legendtextfont,legendTitleFont;
  759.   const char * curvetitle;
  760.   const char * legendtitle = [legendTitle stringValueAt:0];
  761.   int j;
  762.   int ncurves = [plotParam nCurvesTotal];
  763.   float maxcurvetitlewid = 0;
  764.   float yhgt;
  765.  
  766.   if ([plotParam shouldChangeLegendFont]) {
  767.     newLegendFont = [theFontManager convertFont:[theFontManager selFont]];
  768.   }
  769.  
  770.   if (newLegendFont) {
  771.     legendtextfont = [Font newFont:[newLegendFont name] 
  772.                    size:[newLegendFont pointSize]
  773.                    style:[newLegendFont style]
  774.                    matrix:NX_IDENTITYMATRIX];
  775.     yhgt = [newLegendFont pointSize];    /* height of text */
  776.   }
  777.   else {
  778.     legendtextfont =
  779.       [Font newFont:"Helvetica" size:DEFAULTFONTSIZE
  780.             style:0 matrix:NX_IDENTITYMATRIX];
  781.     yhgt = DEFAULTFONTSIZE;
  782.   }
  783.  
  784.   [legendtextfont set];
  785.  
  786.   legendbox.size.height = 10.0;
  787.  
  788.   for (j=0; j<ncurves; j++) {
  789.     /* check for no lines and no symbols */
  790.     if( ([plotParam providelinestyle:j] == NOLINE)  &&
  791.        ( [plotParam providesymbolstyle:j] == NOSYMBOL) )continue;
  792.     curvetitle = [legendForm stringValueAt:j];
  793.     /* skip this curve if there is no title: */
  794.     if ([legendtextfont getWidthOf:curvetitle] == 0.0) continue;
  795.     maxcurvetitlewid = MAX(maxcurvetitlewid,
  796.                [legendtextfont getWidthOf:curvetitle]);
  797.     legendbox.size.height += yhgt; /* increment by yhgt for every curve */
  798.   }
  799.  
  800.   /* check on legend title width also */
  801.  
  802.   if ([plotParam shouldChangeLegendTitleFont]) {
  803.     newLegendTitleFont = [theFontManager convertFont:[theFontManager selFont]];
  804.   }
  805.  
  806.   if (newLegendTitleFont) {
  807.     legendTitleFont = [Font newFont:[newLegendTitleFont name] 
  808.                     size:[newLegendTitleFont pointSize]
  809.                     style:[newLegendTitleFont style]
  810.                     matrix:NX_IDENTITYMATRIX];
  811.     yhgt = [newLegendTitleFont pointSize];    /* height of text */
  812.   }
  813.   else {
  814.     legendTitleFont =
  815.       [Font newFont:"Helvetica" size:DEFAULTFONTSIZE
  816.             style:0 matrix:NX_IDENTITYMATRIX];
  817.     yhgt = DEFAULTFONTSIZE;
  818.   }
  819.  
  820.   [legendTitleFont set];
  821.   maxcurvetitlewid = MAX(maxcurvetitlewid,
  822.                [legendTitleFont getWidthOf:legendtitle]);
  823.  
  824.   legendbox.size.width = 5.0 + 40.0 + 5.0 + maxcurvetitlewid + 5.0;
  825.   /* legendboxwidth = L. margin + 40 + space + curve title + R. margin */
  826.   if([legendTitleFont getWidthOf:legendtitle] != 0)
  827.     legendbox.size.height = legendbox.size.height + 2.0*yhgt - 4.0;
  828.   return self;
  829. }
  830.  
  831. - drawLegend:sender
  832. {
  833.   id legendtextfont,legendTitleFont;
  834.   const char * curvetitle;
  835.   const char * legendtitle = [legendTitle stringValueAt:0];
  836.   int j;
  837.   int ncurves = [plotParam nCurvesTotal];
  838.   float yhgt;
  839.  
  840.   if ([plotParam shouldChangeLegendFont]) {
  841.     newLegendFont = [theFontManager convertFont:[theFontManager selFont]];
  842.   }
  843.  
  844.   /* get legend text font initialized */
  845.   if (newLegendFont) {
  846.     legendtextfont = [Font newFont:[newLegendFont name] 
  847.                    size:[newLegendFont pointSize]
  848.                    style:[newLegendFont style]
  849.                    matrix:NX_IDENTITYMATRIX];
  850.     yhgt = [newLegendFont pointSize];
  851.   }
  852.   else {
  853.     legendtextfont =
  854.       [Font newFont:"Helvetica" size:DEFAULTFONTSIZE
  855.             style:0 matrix:NX_IDENTITYMATRIX];
  856.     yhgt = DEFAULTFONTSIZE;
  857.   }
  858.  
  859.   [legendtextfont set];
  860.  
  861.   drawingLegendLines = YES;
  862.   [self xdata:0];
  863.   [self ydata:0];
  864.  
  865.   NXSetColor([plotParam provideBackgroundColor]);
  866.  
  867.   if ([legendOpaque state] == 0) {
  868.     NXRectFill(&legendbox);
  869.   }
  870.  
  871.   [sender drawLines:sender :NO :NO];    /* legend lines, linear axes always */
  872.  
  873.   NXSetColor([plotParam provideTextColor]);
  874.  
  875.   for (j=0; j<ncurves; j++) {
  876.     /* check for no lines and no symbols */
  877.     if ( ([plotParam providelinestyle:j] == NOLINE)  &&
  878.     ([plotParam providesymbolstyle:j] == NOSYMBOL) ) continue;
  879.     curvetitle = [legendForm stringValueAt:j];
  880.     /* skip this curve if there is no title: */
  881.     if ([legendtextfont getWidthOf:curvetitle] == 0.0) continue;
  882.     PSmoveto(xlegend[1]*ppxunit + 5.0, 
  883.          *(*(ylegend+j)+0) * ppyunit - 0.33*yhgt);
  884.     PSshow((char *)curvetitle);
  885.   }
  886.  
  887.   if ([plotParam shouldChangeLegendTitleFont]) {
  888.     newLegendTitleFont = [theFontManager convertFont:[theFontManager selFont]];
  889.   }
  890.  
  891.   if (newLegendTitleFont) {
  892.     legendTitleFont = [Font newFont:[newLegendTitleFont name] 
  893.                     size:[newLegendTitleFont pointSize]
  894.                     style:[newLegendTitleFont style]
  895.                     matrix:NX_IDENTITYMATRIX];
  896.     yhgt = [newLegendTitleFont pointSize];    /* height of text */
  897.   }
  898.   else {
  899.     legendTitleFont =
  900.       [Font newFont:"Helvetica" size:DEFAULTFONTSIZE
  901.             style:0 matrix:NX_IDENTITYMATRIX];
  902.     yhgt = DEFAULTFONTSIZE;
  903.   }
  904.  
  905.   [legendTitleFont set];
  906.   PSmoveto(xlegend[0]*ppxunit - 5.0 + legendbox.size.width/2.0
  907.        - 0.5*[legendTitleFont getWidthOf:legendtitle],
  908.        *(*(ylegend+0)+0) * ppyunit + yhgt);
  909.   PSshow((char *)legendtitle);
  910.   if([legendBoxOnOff state]) {
  911.     PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 :
  912.          [accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW :
  913.                                          LINE_WIDTH_IF_PRINTING_COLOR);
  914.     PSrectstroke(legendbox.origin.x, legendbox.origin.y,
  915.          legendbox.size.width, legendbox.size.height);
  916.   }
  917.   drawingLegendLines = NO;
  918.   drawingLegendSymbols = YES;
  919.   [sender drawSymbols:sender :NO :NO];    /* legend symbols, linear axes always */
  920.   drawingLegendSymbols = NO;
  921.  
  922.   return self;
  923. }
  924.  
  925. - clear:sender
  926. {
  927.   /*
  928.    * Derek Lisoski (dlisoski@cco.caltech.edu) suggests not drawing the
  929.    * opaque background rectangle when you print or save a file.  Then
  930.    * when you read the saved plots into a separate drawing program you
  931.    * can overlay multiple plots or get the plots arbitrarily close to
  932.    * each other.  There may be some problems with background colors
  933.    * when importing into various applications (Create or Draw, e.g.)
  934.    */
  935.   if (NXDrawingStatus == NX_DRAWING || [opaqueBackgroundButton state] == 0) {
  936.     NXRectFill(&bounds);
  937.   /* for color; had NXEraseRect, but that always fills with white    */
  938.   }
  939.   return self;
  940. }
  941.  
  942. /*
  943.  * This routine assumes it is called with xmin != xmax and ymin != ymax.
  944.  * Bad things may happen if this is not the case.
  945.  * The input parameters are assumed to be in pixel coordinates.
  946.  */
  947. - drawTicMarks:(float)xmin :(float)xmax :(float)ymin :(float)ymax
  948. {
  949.   float pattern0[] = {};    /* solid      */
  950.   float pattern2[] = {1.0, 3.0}; /* dot        */
  951.   double xinc_unscaled = [plotParam provideXinc];
  952.   double yinc_unscaled = [plotParam provideYinc];
  953.   double xmin_unscaled = [plotParam provideXmin];
  954.   double ymin_unscaled = [plotParam provideYmin];
  955.   double xmax_unscaled = [plotParam provideXmax];
  956.   double ymax_unscaled = [plotParam provideYmax];
  957.   /* It is useful in some fairly extreme cases to have the increments
  958.    * not in pixel coordinates (otherwise can get "bad" labels).
  959.    */
  960.   char ticlabel[32];
  961.   id ticfont, ticfont1;
  962.   float x, y, ticloc_rat, xticloc, yticloc;
  963.   BOOL drawGrid = [gridOnOff state];
  964.   BOOL xaxislog = [plotParam xaxisLog];
  965.   BOOL yaxislog = [plotParam yaxisLog];
  966.   BOOL drawMinorTics = [minorTicMarksOnOff state];
  967.   int  ticLocation;        /* 0=axes, 1=2 sides, 2=4 sides */
  968.   float  ticmarklen = [ticMarkLengthText floatValue];
  969.   int    nmin, ninc, nmax, j, i;
  970.   float  ticloc, xwid, yhgt, yhgt1;
  971.   double first;
  972.   int    nlabels;
  973.   int    axformat[3];
  974.  
  975.  
  976.   if (strncmp([ticMarkLocation title], "Axes", 4) == 0)
  977.     ticLocation = 0;
  978.   else if (strncmp([ticMarkLocation title], "Frame (2 sides)", 15) == 0)
  979.     ticLocation = 1;
  980.   else
  981.     ticLocation = 2;
  982.  
  983.   if ([plotParam shouldChangeTicLabelFont]) {
  984.     newTicLabelFont = [theFontManager convertFont:[theFontManager selFont]];
  985.   }
  986.  
  987.   if (newTicLabelFont) {
  988.     ticfont = [Font newFont:[newTicLabelFont name] 
  989.                 size:[newTicLabelFont pointSize]
  990.                 style:[newTicLabelFont style]
  991.                 matrix:NX_IDENTITYMATRIX];
  992.     yhgt = [newTicLabelFont pointSize];
  993.  
  994.     yhgt1 = (yhgt >= 10.0? yhgt - 2.0 : 8.0);
  995.     ticfont1 = [Font newFont:[newTicLabelFont name] 
  996.                  size:yhgt1
  997.                  style:[newTicLabelFont style]
  998.                  matrix:NX_IDENTITYMATRIX];
  999.   }
  1000.   else {
  1001.     ticfont = [Font newFont:"Helvetica" size:DEFAULTFONTSIZE
  1002.                 style:0 matrix:NX_IDENTITYMATRIX];
  1003.     ticfont1 =
  1004.       [Font newFont:"Helvetica" size:10.0 style:0 matrix:NX_IDENTITYMATRIX];
  1005.     yhgt = DEFAULTFONTSIZE;
  1006.   }
  1007.  
  1008.   /* get tic font initialized */
  1009.   [ticfont set];
  1010.  
  1011.   PSnewpath();
  1012.   PSsetlinewidth([ticMarkThicknessText floatValue]);
  1013.   if (xaxislog) {        /* x-axis is logarithmic */
  1014.     nmin = (int)floor((double)(xmin/ppxunit));
  1015.     nmax = (int)ceil((double)(xmax/ppxunit));
  1016.     ninc = (int)rint(log10(xinc_unscaled));
  1017.     if (ninc == 0) ninc = 1;    /* avoid infinite loop */
  1018.     if ([handFormatXaxis state] == 1) {
  1019.       axformat[0] = [xFormatLeft intValue];
  1020.       axformat[1] = [xFormatRight intValue];
  1021.       axformat[2] = [xFormatExponent intValue];
  1022.     }
  1023.     for (j=nmin; j<=nmax; j+=ninc) {
  1024.       if ( (float)j * ppxunit >= xmin &&  (float)j * ppxunit <= xmax ) {
  1025.     if (drawGrid) {
  1026.       PSsetlinewidth([gridThicknessText floatValue]);
  1027.       PSmoveto((float)j * ppxunit, ymin);
  1028.       if ([gridDotted state])
  1029.         PSsetdash(pattern2, 2, 0.0);
  1030.       else
  1031.         PSsetdash(pattern0, 0, 0.0);
  1032.       PSrlineto(0.0, ABS(ymax-ymin));
  1033.       PSstroke();
  1034.       PSsetlinewidth([ticMarkThicknessText floatValue]);
  1035.     }
  1036.     PSmoveto((float)j * ppxunit, ymin - ticmarklen*6.0); /* big tic mark */
  1037.     PSsetdash(pattern0, 0, 0.0);
  1038.     PSrlineto(0.0, ticmarklen*6.0);
  1039.     PSstroke();
  1040.     if (ticLocation == 2) {    /* tics on right and top */
  1041.       PSmoveto((float)j * ppxunit, ymax); /* big tic mark */
  1042.       PSsetdash(pattern0, 0, 0.0);
  1043.       PSrlineto(0.0, ticmarklen*6.0);
  1044.       PSstroke();
  1045.     }
  1046.     if ([handFormatXaxis state] == 0) {
  1047.       ticloc_rat = yhgt/DEFAULTFONTSIZE;
  1048.       PSmoveto((float)j * ppxunit - ticloc_rat*8.0,
  1049.            ymin - MAX(24.0*ticloc_rat,
  1050.                   24.0*ticloc_rat*(ticmarklen+10.0)/10.0));
  1051.       PSshow("10");
  1052.       [ticfont1 set];
  1053.       PSmoveto((float)j * ppxunit + ticloc_rat*4.0,
  1054.            ymin - MAX(16.0*ticloc_rat,
  1055.                    8.0*ticloc_rat*(2.0 + 0.3*ticmarklen)));
  1056.       sprintf(ticlabel, "%-5d", j);
  1057.       PSshow(ticlabel);
  1058.       [ticfont set];
  1059.     }
  1060.     else {
  1061.       x = (float) pow((double)10.0, (double)j);
  1062.       handformat(x, ticlabel, axformat);
  1063.       xwid = [ticfont getWidthOf:ticlabel];
  1064.       PSmoveto((float)j * ppxunit - xwid/2.0,
  1065.             ymin - yhgt - MAX(5.0, 5.0*ticmarklen));
  1066.       PSshow(ticlabel);
  1067.     }
  1068.       }
  1069.       if (drawMinorTics) {
  1070.     for (i=2; i<=9; i++) {
  1071.       ticloc = (float)j * ppxunit + ppxunit*(float)log10((double)i);
  1072.       if ( ticloc>xmin && ticloc<xmax ) {
  1073.         PSmoveto(ticloc, ymin - ticmarklen*3.0); /* small tic mark */
  1074.         PSrlineto(0.0, ticmarklen*3.0);
  1075.         PSstroke();
  1076.         if (ticLocation == 2) { /* tics on right and top */
  1077.           PSmoveto(ticloc, ymax); /* small tic mark */
  1078.           PSrlineto(0.0, ticmarklen*3.0);
  1079.           PSstroke();
  1080.         }
  1081.       }
  1082.     }
  1083.       }
  1084.     }
  1085.   }
  1086.   else {            /* x-axis is linear */
  1087.     yticloc = (ticLocation > 0 ? ymin : 2.0*ticmarklen) ;
  1088.     /* If inc is big, skip tic marks entirely */
  1089.     if (fabs(xinc_unscaled) < fabs(xmax_unscaled - xmin_unscaled)) {
  1090.       count_labels(&nlabels, &first, xmin_unscaled, xinc_unscaled, xmax_unscaled);
  1091.       if ([handFormatXaxis state] == 1) {
  1092.     axformat[0] = [xFormatLeft intValue];
  1093.     axformat[1] = [xFormatRight intValue];
  1094.     axformat[2] = [xFormatExponent intValue];
  1095.       }
  1096.       else {
  1097.     autoformat(xmin_unscaled, xinc_unscaled, xmax_unscaled, axformat);
  1098.     [xFormatLeft setIntValue:axformat[0]];
  1099.     [xFormatRight setIntValue:axformat[1]];
  1100.     [xFormatExponent setIntValue:axformat[2]];
  1101.       }
  1102.       /*
  1103.        * next loop starts at -1 because there may be room for minor tic
  1104.        * marks to the left of the first major tic mark (after a zoom, e.g.)
  1105.        */
  1106.       for (i = -1; i < nlabels; i++) {
  1107.     /* Special test here for what should be exact 0 (but isn't sometimes
  1108.      * due to floating-point arithmetic.
  1109.      */
  1110.     if (fabs(first/xinc_unscaled + (float)i) < 4.0e-7) { /* ugly */
  1111.       x = 0.0;
  1112.     }
  1113.     else {
  1114.       x = (first + (xinc_unscaled)*(float)i) * ppxunit;
  1115.     }
  1116.     if (x >= xmin) {    /* ensure major tic mark won't be off edge */
  1117.       if (drawGrid) {
  1118.         PSsetlinewidth([gridThicknessText floatValue]);
  1119.         PSmoveto(x, ymin);
  1120.         if ([gridDotted state])
  1121.           PSsetdash(pattern2, 2, 0.0);
  1122.         else
  1123.           PSsetdash(pattern0, 0, 0.0);
  1124.         PSrlineto(0.0, ABS(ymax-ymin));
  1125.         PSstroke();
  1126.         PSsetlinewidth([ticMarkThicknessText floatValue]);
  1127.       }
  1128.       /* Nothing at 0 if we're putting tic marks on axes:  */
  1129.       if (ticLocation > 0 || x != 0.0) {
  1130.         PSmoveto(x, yticloc - ticmarklen*4.0);
  1131.         PSsetdash(pattern0, 0, 0.0);
  1132.         PSrlineto(0.0, ticmarklen*4.0);
  1133.         PSstroke();
  1134.             if (ticLocation == 2) { /* tics on right and top */
  1135.           PSmoveto(x, ymax);
  1136.           PSsetdash(pattern0, 0, 0.0);
  1137.           PSrlineto(0.0, ticmarklen*4.0);
  1138.           PSstroke();
  1139.             }
  1140.         handformat(x/ppxunit, ticlabel, axformat);
  1141.         xwid = [ticfont getWidthOf:ticlabel];
  1142.         PSmoveto(x - xwid/2.0, yticloc - yhgt - MAX(5.0, 5.0*ticmarklen));
  1143.         PSshow(ticlabel);
  1144.       }
  1145.     }
  1146.     if (drawMinorTics) {
  1147.           if (ticLocation > 0) { /* tic marks on frame */
  1148.         for (j=1; j<=9; j++) {
  1149.           ticloc = x + ((xinc_unscaled/10.0)*(float)j)*ppxunit;
  1150.           if (ticloc>xmin && ticloc<xmax) {
  1151.             PSmoveto(ticloc, yticloc - ticmarklen*2.0);
  1152.             PSrlineto(0.0, ticmarklen*2.0);
  1153.             PSstroke();
  1154.                 if (ticLocation == 2) {    /* tics on right and top */
  1155.                 PSmoveto(ticloc, ymax);
  1156.               PSrlineto(0.0, ticmarklen*2.0);
  1157.               PSstroke();
  1158.                 }
  1159.               }
  1160.             }
  1161.           }
  1162.           else {
  1163.         for (j=1; j<=9; j++) {
  1164.           ticloc = x + ((xinc_unscaled/10.0)*(float)j)*ppxunit;
  1165.           if (ticloc>xmin && ticloc<xmax) {
  1166.             PSmoveto(ticloc, -ticmarklen);
  1167.             PSrlineto(0.0, 2.0*ticmarklen);
  1168.             PSstroke();
  1169.               }
  1170.             }
  1171.           }
  1172.     }
  1173.       }
  1174.     }
  1175.   }
  1176.   if (yaxislog) {        /* y-axis is logarithmic */
  1177.     nmin = (int)floor((double)(ymin/ppyunit));
  1178.     nmax = (int)ceil((double)(ymax/ppyunit));
  1179.     ninc = (int)rint(log10(yinc_unscaled));
  1180.     if (ninc == 0) ninc = 1;    /* avoid infinite loop */
  1181.     if ([handFormatYaxis state] == 1) {
  1182.       axformat[0] = [yFormatLeft intValue];
  1183.       axformat[1] = [yFormatRight intValue];
  1184.       axformat[2] = [yFormatExponent intValue];
  1185.     }
  1186.     for (j=nmin; j<=nmax; j+=ninc) {
  1187.       if ( (float)j * ppyunit >= ymin &&  (float)j * ppyunit <= ymax ) {
  1188.     if (drawGrid) {
  1189.       PSsetlinewidth([gridThicknessText floatValue]);
  1190.       PSmoveto(xmin, (float)j * ppyunit);
  1191.       if ([gridDotted state])
  1192.         PSsetdash(pattern2, 2, 0.0);
  1193.       else
  1194.         PSsetdash(pattern0, 0, 0.0);
  1195.       PSrlineto(ABS(xmax-xmin), 0.0);
  1196.       PSstroke();
  1197.       PSsetlinewidth([ticMarkThicknessText floatValue]);
  1198.     }
  1199.     PSmoveto(xmin - ticmarklen*6.0, (float)j * ppyunit); /* big tic mark */
  1200.     PSsetdash(pattern0, 0, 0.0);
  1201.     PSrlineto(ticmarklen*6.0, 0.0);
  1202.     PSstroke();
  1203.         if (ticLocation == 2) {    /* tics on right and top */
  1204.       PSmoveto(xmax, (float)j * ppyunit); /* big tic mark */
  1205.       PSsetdash(pattern0, 0, 0.0);
  1206.       PSrlineto(ticmarklen*6.0, 0.0);
  1207.       PSstroke();
  1208.         }
  1209.     if ([handFormatYaxis state] == 0) {
  1210.       ticloc_rat = yhgt/DEFAULTFONTSIZE;
  1211.       PSmoveto(xmin - MAX(40.0*ticloc_rat,
  1212.                   40.0*ticloc_rat*(ticmarklen+10.0)/10.0),
  1213.            (float)j * ppyunit - ticloc_rat*7.0);
  1214.       PSshow("10");
  1215.       [ticfont1 set];
  1216.       PSmoveto(xmin - MAX(24.0*ticloc_rat,
  1217.                    4.0*ticloc_rat*(ticmarklen + 6.0)),
  1218.            (float)j * ppyunit - ticloc_rat*1.0);
  1219.       sprintf(ticlabel, "%-5d", j);
  1220.       PSshow(ticlabel);
  1221.       [ticfont set];
  1222.     }
  1223.     else {
  1224.       y = (float) pow((double)10.0, (double)j);
  1225.       handformat(y, ticlabel, axformat);
  1226.       xwid = [ticfont getWidthOf:ticlabel];
  1227.       PSmoveto(xmin - xwid - MAX(10.0, 5.0*ticmarklen),
  1228.                (float)j * ppyunit + 2.0 - yhgt/2.0);
  1229.       PSshow(ticlabel);
  1230.     }
  1231.       }
  1232.       if (drawMinorTics) {
  1233.     for (i=2; i<=9; i++) {
  1234.       ticloc = (float)j * ppyunit + ppyunit*(float)log10((double)i);
  1235.       if (ticloc>ymin && ticloc<ymax) {
  1236.         PSmoveto(xmin - ticmarklen*3.0, ticloc); /* small tic mark */
  1237.         PSrlineto(ticmarklen*3.0, 0.0);
  1238.         PSstroke();
  1239.             if (ticLocation == 2) { /* tics on right and top */
  1240.           PSmoveto(xmax, ticloc); /* small tic mark */
  1241.           PSrlineto(ticmarklen*3.0, 0.0);
  1242.           PSstroke();
  1243.             }
  1244.       }
  1245.     }
  1246.       }
  1247.     }
  1248.   }
  1249.   else {            /* y-axis is linear */
  1250.     xticloc = (ticLocation > 0 ? xmin : 2.0*ticmarklen) ;
  1251.     /* If inc is big, skip tic marks entirely */
  1252.     if (fabs(yinc_unscaled) < fabs(ymax_unscaled - ymin_unscaled)) {
  1253.       count_labels(&nlabels, &first, ymin_unscaled, yinc_unscaled, ymax_unscaled);
  1254.       if ([handFormatYaxis state] == 1) {
  1255.     axformat[0] = [yFormatLeft intValue];
  1256.     axformat[1] = [yFormatRight intValue];
  1257.     axformat[2] = [yFormatExponent intValue];
  1258.       }
  1259.       else {
  1260.     autoformat(ymin_unscaled, yinc_unscaled, ymax_unscaled, axformat);
  1261.     [yFormatLeft setIntValue:axformat[0]];
  1262.     [yFormatRight setIntValue:axformat[1]];
  1263.     [yFormatExponent setIntValue:axformat[2]];
  1264.       }
  1265.       /*
  1266.        * next loop starts at -1 because there may be room for minor tic
  1267.        * marks to the left of the first major tic mark (after a zoom, e.g.)
  1268.        */
  1269.       for (i = -1; i < nlabels; i++) {
  1270.     /* Special test here for what should be exact 0 (but isn't sometimes
  1271.      * due to floating-point arithmetic.
  1272.      */
  1273.     if (fabs(first/yinc_unscaled + (float)i) < 4.0e-7) { /* ugly */
  1274.       y = 0.0;
  1275.     }
  1276.     else {
  1277.       y = (first + (yinc_unscaled)*(float)i) * ppyunit;
  1278.     }
  1279.     if (y >= ymin) {    /* ensure major tic mark won't be off edge */
  1280.       if (drawGrid) {
  1281.         PSsetlinewidth([gridThicknessText floatValue]);
  1282.         PSmoveto(xmin, y);
  1283.         if ([gridDotted state])
  1284.           PSsetdash(pattern2, 2, 0.0);
  1285.         else
  1286.           PSsetdash(pattern0, 0, 0.0);
  1287.         PSrlineto(ABS(xmax-xmin), 0.0);
  1288.         PSstroke();
  1289.         PSsetlinewidth([ticMarkThicknessText floatValue]);
  1290.       }
  1291.       /* Nothing at 0 if we're putting tic marks on axes:  */
  1292.       if (ticLocation > 0 || y != 0.0) {
  1293.         PSmoveto(xticloc - ticmarklen*4.0, y);
  1294.         PSsetdash(pattern0, 0, 0.0);
  1295.         PSrlineto(ticmarklen*4.0, 0.0);
  1296.         PSstroke();
  1297.             if (ticLocation == 2) { /* tics on right and top */
  1298.               PSmoveto(xmax, y);
  1299.           PSsetdash(pattern0, 0, 0.0);
  1300.           PSrlineto(ticmarklen*4.0, 0.0);
  1301.           PSstroke();
  1302.             }
  1303.         handformat(y/ppyunit, ticlabel, axformat);
  1304.         xwid = [ticfont getWidthOf:ticlabel];
  1305.         PSmoveto(xticloc - xwid - MAX(10.0, 5.0*ticmarklen),
  1306.              y + 2.0 - yhgt/2.0);
  1307.         PSshow(ticlabel);
  1308.       }
  1309.     }
  1310.     if (drawMinorTics) {
  1311.           if (ticLocation > 0) { /* tics on frame */
  1312.         for (j=1; j<=9; j++) {
  1313.           ticloc = y + ((yinc_unscaled/10.0)*(float)j)*ppyunit;
  1314.           if (ticloc>ymin && ticloc<ymax) {
  1315.             PSmoveto(xticloc - ticmarklen*2.0, ticloc);
  1316.             PSrlineto(ticmarklen*2.0, 0.0);
  1317.             PSstroke();
  1318.                 if (ticLocation == 2) {    /* tics on right and top */
  1319.               PSmoveto(xmax, ticloc);
  1320.               PSrlineto(ticmarklen*2.0, 0.0);
  1321.               PSstroke();
  1322.                 }
  1323.               }
  1324.         }
  1325.       }
  1326.           else {
  1327.             for (j=1; j<=9; j++) {
  1328.           ticloc = y + ((yinc_unscaled/10.0)*(float)j)*ppyunit;
  1329.           if (ticloc>ymin && ticloc<ymax) {
  1330.             PSmoveto(-ticmarklen, ticloc);
  1331.             PSrlineto(2.0*ticmarklen, 0.0);
  1332.             PSstroke();
  1333.               }
  1334.         }
  1335.       }
  1336.     }
  1337.       }
  1338.     }
  1339.   }
  1340.   return self;
  1341. }
  1342.  
  1343. - drawSelf: (const NXRect *)rects :(int)rectCount
  1344. {
  1345.   float  xmin = (float)[plotParam provideXmin];
  1346.   float  xmax = (float)[plotParam provideXmax];
  1347.   float  ymin = (float)[plotParam provideYmin];
  1348.   float  ymax = (float)[plotParam provideYmax];
  1349.  
  1350.   [self startPlot];
  1351.  
  1352.   if ([plotParam nFiles] == 0) return self; /* no data */
  1353.  
  1354.   if (xmin==xmax || ymin==ymax) return self; /* avoid division by zero */
  1355.  
  1356.   PSgsave();
  1357.  
  1358.   PSsetmiterlimit(1);        // suggested by Carrick Talmadge
  1359.                                 // (clt@eotvos.physics.purdue.edu)
  1360.  
  1361.   PStranslate(XOFFSET, YOFFSET);
  1362.  
  1363.   if ([plotParam xaxisLog]) {    /* x-axis is logarithmic */
  1364.     ppxunit = 0.9*(bounds.size.width-XOFFSET)
  1365.       /(float)log10((double)(xmax/xmin));
  1366.     xmin = (float)log10((double)xmin) * ppxunit;
  1367.     xmax = (float)log10((double)xmax) * ppxunit;
  1368.   }
  1369.   else {            /* x-axis is linear */
  1370.     ppxunit = 0.9*(bounds.size.width-XOFFSET)/(xmax-xmin);
  1371.     xmin = xmin*ppxunit;    /* drawing is all in pixel coordinates */
  1372.     xmax = xmax*ppxunit;
  1373.   }
  1374.   if ([plotParam yaxisLog]) {    /* y-axis is logarithmic */
  1375.     ppyunit = 0.9*(bounds.size.height-YOFFSET)
  1376.       /(float)log10((double)(ymax/ymin));
  1377.     ymin = (float)log10((double)ymin) * ppyunit;
  1378.     ymax = (float)log10((double)ymax) * ppyunit;
  1379.   }
  1380.   else {            /* y-axis is linear */
  1381.     ppyunit = 0.9*(bounds.size.height-YOFFSET)/(ymax-ymin);
  1382.     ymin = ymin*ppyunit;
  1383.     ymax = ymax*ppyunit;
  1384.   }
  1385.   PStranslate(-xmin, -ymin);
  1386.   // inner "frame" box
  1387.   if ([frameBoxOnOff state]) {
  1388.     PSsetlinewidth([frameBoxThicknessText floatValue]);
  1389.     PSnewpath();
  1390.     PSmoveto(xmin, ymin);
  1391.     PSlineto(xmax, ymin);
  1392.     PSlineto(xmax, ymax);
  1393.     PSlineto(xmin, ymax);
  1394.     PSclosepath();
  1395.     PSstroke();
  1396.  /* reset line width */
  1397.     PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 :
  1398.          [accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW :
  1399.                                          LINE_WIDTH_IF_PRINTING_COLOR);
  1400.   }
  1401.  
  1402.   if ([axesOnOff state]) {
  1403.     PSnewpath();
  1404.     PSsetlinewidth([axisThicknessText floatValue]);
  1405.     PSmoveto(0.0, ymin);
  1406.     PSlineto(0.0, ymax);
  1407.     PSstroke();
  1408.     PSmoveto(xmin, 0.0);
  1409.     PSlineto(xmax, 0.0);
  1410.     PSstroke();
  1411.   }
  1412.  
  1413.   if ([majorTicMarksOnOff state])
  1414.     [self drawTicMarks:xmin :xmax :ymin :ymax]; /* do this before clipping */
  1415.  
  1416.   /* MIN and MAX below in case xmin > xmax  and/or  ymin > ymax. */
  1417.   PSrectclip(MIN(xmin,xmax), MIN(ymin,ymax), ABS(xmax-xmin), ABS(ymax-ymin));
  1418.  
  1419.   [self drawLines:plotParam :[plotParam xaxisLog] :[plotParam yaxisLog]];
  1420.   [self drawSymbols:plotParam :[plotParam xaxisLog] :[plotParam yaxisLog]];
  1421.  
  1422.   [self drawErrorBars :[plotParam xaxisLog] :[plotParam yaxisLog]];
  1423.  
  1424.   PSgrestore();
  1425.   if ([legendOnOff state]) {
  1426.     [self startLegend];
  1427.     [self drawLegend:self];
  1428.   }
  1429.  
  1430.   return self;
  1431. }
  1432.  
  1433. - doPrinting:sender
  1434. {
  1435.  
  1436.   id myPrintPanel = [PrintPanel new];
  1437.  
  1438.   [ [NXApp printInfo] setMarginLeft:72.0 right:72.0 top:72.0 bottom:72.0 ];
  1439.  
  1440.   if (bounds.size.height > bounds.size.width) { /* portrait mode */
  1441.     if (bounds.size.height/bounds.size.width > 9.0/6.5) {
  1442.       [ [NXApp printInfo] setScalingFactor: 9.0*72.0/bounds.size.height];
  1443.     }
  1444.     else {
  1445.       [ [NXApp printInfo] setScalingFactor: 6.5*72.0/bounds.size.width];
  1446.     }
  1447.     [ [NXApp printInfo] setOrientation:NX_PORTRAIT andAdjust:YES ];
  1448.   }
  1449.   else {            /* landscape mode */
  1450.     if (bounds.size.width/bounds.size.height > 9.0/6.5) {
  1451.       [ [NXApp printInfo] setScalingFactor: 9.0*72.0/bounds.size.width];
  1452.     }
  1453.     else {
  1454.       [ [NXApp printInfo] setScalingFactor: 6.5*72.0/bounds.size.height];
  1455.     }
  1456.     [ [NXApp printInfo] setOrientation:NX_LANDSCAPE andAdjust:YES ];
  1457.   }
  1458.  
  1459.   [myPrintPanel setAccessoryView:printPanelAccessory];
  1460.  
  1461.   [self printPSCode:sender];
  1462.   return self;
  1463. }
  1464.  
  1465. - mouseDown:(NXEvent *)e
  1466. /*
  1467.  * This code taken from the Mandelbrot example in /NextDeveloper (and modified).
  1468.  * We implement the mouseDown method so the user can sweep out a section of
  1469.  * the view and select that as the current window in which to view the curve(s).
  1470.  */
  1471. {
  1472.   int looping = YES;
  1473.   int oldMask;
  1474.   NXRect bbox;
  1475.   NXPoint startPoint, currPoint;
  1476.   float xmin, xmax, ymin, ymax;
  1477.   register float t1, t2, t3, t4;
  1478.   int  zooming;
  1479.   BOOL xaxislog = [plotParam xaxisLog];
  1480.   BOOL yaxislog = [plotParam yaxisLog];
  1481.   
  1482.   zooming = ZOOM;
  1483.   if (strncmp([zoomChoice title], "Zoom/Move", 9) == 0)
  1484.     zooming = NOZOOM;
  1485.   else if (strncmp([zoomChoice title], "Move legend", 11) == 0)
  1486.     zooming = MOVELEGEND;
  1487.   else if (strncmp([zoomChoice title], "Move x title", 12) == 0)
  1488.     zooming = MOVEXTITLE;
  1489.   else if (strncmp([zoomChoice title], "Move y title", 12) == 0)
  1490.     zooming = MOVEYTITLE;
  1491.   else if (strncmp([zoomChoice title], "Move main title", 15) == 0)
  1492.     zooming = MOVEMAINTITLE;
  1493.  
  1494.   if (zooming == ZOOM) {    /* really zooming */
  1495.     if (xaxislog) {
  1496.       xmin = (float)log10([plotParam provideXmin]) * ppxunit;
  1497.       xmax = (float)log10([plotParam provideXmax]) * ppxunit;
  1498.     }
  1499.     else {
  1500.       xmin = [plotParam provideXmin] * ppxunit;
  1501.       xmax = [plotParam provideXmax] * ppxunit;
  1502.     }
  1503.     if (yaxislog) {
  1504.       ymin = (float)log10([plotParam provideYmin]) * ppyunit;
  1505.       ymax = (float)log10([plotParam provideYmax]) * ppyunit;
  1506.     }
  1507.     else {
  1508.       ymin = [plotParam provideYmin] * ppyunit;
  1509.       ymax = [plotParam provideYmax] * ppyunit;
  1510.     }
  1511.   
  1512.     oldMask =  [window addToEventMask:NX_MOUSEDRAGGEDMASK];
  1513.     startPoint = e->location;
  1514.     [self convertPoint:&startPoint fromView:nil];
  1515.     NXSetRect(&bbox,startPoint.x,startPoint.y,0.0,0.0);
  1516.     [self lockFocus];
  1517.     while (looping) {
  1518.       e=[NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK];
  1519.       currPoint = e->location;
  1520.       [self convertPoint:&currPoint fromView:nil];
  1521.       bbox.size.width = (currPoint.x - startPoint.x);
  1522.       bbox.size.height = (currPoint.y - startPoint.y);
  1523.       /* Normalize bbox to always have positive width and height */
  1524.       if (bbox.size.width < 0) {
  1525.     bbox.size.width = -bbox.size.width;
  1526.     bbox.origin.x   = startPoint.x - bbox.size.width;
  1527.       }
  1528.       if (bbox.size.height < 0) {
  1529.     bbox.size.height = -bbox.size.height;
  1530.     bbox.origin.y    = startPoint.y - bbox.size.height;
  1531.       }
  1532.  
  1533.       PSnewinstance();
  1534.       if (looping = (e->type == NX_MOUSEDRAGGED)) {
  1535.     PSsetinstance(YES);
  1536.     NXSetColor([plotParam provideTextColor]);
  1537.     NXFrameRect(&bbox);
  1538.     PSsetinstance(NO);
  1539.       }
  1540.     }
  1541.     [self unlockFocus];
  1542.     [window setEventMask:oldMask];
  1543.     if ((bbox.size.width > 0) && (bbox.size.height > 0)) {
  1544.       /* At this point, bbox is in window coordinates.  Convert to curve
  1545.        * coordinates based on this view's coordinates and the bounding box.
  1546.        */
  1547.  
  1548.       xmin = xmin + (bbox.origin.x - XOFFSET);
  1549.       xmax = xmin + bbox.size.width;
  1550.       ymin = ymin + (bbox.origin.y - YOFFSET);
  1551.       ymax = ymin + bbox.size.height;
  1552.  
  1553.       /* save old min/max -- must adjust if log axis */
  1554.       t1 = xmin/ppxunit;
  1555.       t2 = xmax/ppxunit;
  1556.       t3 = ymin/ppyunit;
  1557.       t4 = ymax/ppyunit;
  1558.       if (xaxislog) {
  1559.     t1 = (float) pow((double)10.0, (double)t1);
  1560.     t2 = (float) pow((double)10.0, (double)t2);
  1561.       }
  1562.       if (yaxislog) {
  1563.     t3 = (float) pow((double)10.0, (double)t3);
  1564.     t4 = (float) pow((double)10.0, (double)t4);
  1565.       }
  1566.       [plotParam stackOldMinMax:t1 :t2 :t3 :t4];
  1567.  
  1568.       if (xaxislog) {
  1569.     [plotParam resetXmin:pow((double)10.0, (double)(xmin/ppxunit))];
  1570.     [plotParam resetXmax:pow((double)10.0, (double)(xmax/ppxunit))];
  1571.       }
  1572.       else {
  1573.     [plotParam resetXmin:(double)(xmin/ppxunit)];
  1574.     [plotParam resetXmax:(double)(xmax/ppxunit)];
  1575.       }
  1576.       if (yaxislog) {
  1577.     [plotParam resetYmin:pow((double)10.0, (double)(ymin/ppyunit))];
  1578.     [plotParam resetYmax:pow((double)10.0, (double)(ymax/ppyunit))];
  1579.       }
  1580.       else {
  1581.     [plotParam resetYmin:(double)(ymin/ppyunit)];
  1582.     [plotParam resetYmax:(double)(ymax/ppyunit)];
  1583.       }
  1584.  
  1585.       /*  Call [self display] to force current values of xmin/xmax/ymin/ymax
  1586.        *  to take effect (otherwise xmin, etc., get incremented too many times).
  1587.        */
  1588.       [plotParam drawPlotButton:1];
  1589.       [self display];
  1590.       [plotParam drawPlotButton:0];
  1591.     }
  1592.   }
  1593.   else if (zooming >= MOVELEGEND) {
  1594.  
  1595.     oldMask =  [window addToEventMask:NX_MOUSEDRAGGEDMASK];
  1596.     startPoint = e->location;
  1597.     [self convertPoint:&startPoint fromView:nil];
  1598.     if (zooming == MOVELEGEND) {
  1599.       NXSetRect(&bbox,startPoint.x,startPoint.y,
  1600.         legendbox.size.width,legendbox.size.height);
  1601.     }
  1602.     else if (zooming == MOVEXTITLE) {
  1603.       NXSetRect(&bbox,startPoint.x,startPoint.y,
  1604.         xtitlebox.size.width,xtitlebox.size.height);
  1605.     }
  1606.     else if (zooming == MOVEYTITLE) {
  1607.       NXSetRect(&bbox,startPoint.x,startPoint.y,
  1608.         ytitlebox.size.width,ytitlebox.size.height);
  1609.     }
  1610.     else if (zooming == MOVEMAINTITLE) {
  1611.       NXSetRect(&bbox,startPoint.x,startPoint.y,
  1612.         maintitlebox.size.width,maintitlebox.size.height);
  1613.     }
  1614.     [self lockFocus];
  1615.     while (looping) {
  1616.       e=[NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK];
  1617.       currPoint = e->location;
  1618.       [self convertPoint:&currPoint fromView:nil];
  1619.       bbox.origin.x = currPoint.x;
  1620.       bbox.origin.y = currPoint.y;
  1621.       PSnewinstance();
  1622.       if (looping = (e->type == NX_MOUSEDRAGGED)) {
  1623.     PSsetinstance(YES);
  1624.     NXSetColor([plotParam provideTextColor]);
  1625.     NXFrameRect(&bbox);
  1626.     PSsetinstance(NO);
  1627.       }
  1628.     }
  1629.     [self unlockFocus];
  1630.     [window setEventMask:oldMask];
  1631.     /*
  1632.      * At this point, bbox is in window coordinates. 
  1633.      */
  1634.     if (zooming == MOVELEGEND) {
  1635.       legendbox.origin.x = bbox.origin.x;
  1636.       legendbox.origin.y = bbox.origin.y;
  1637.     }
  1638.     else if (zooming == MOVEXTITLE) {
  1639.       xtitlebox.origin.x = bbox.origin.x;
  1640.       xtitlebox.origin.y = bbox.origin.y;
  1641.     }
  1642.     else if (zooming == MOVEYTITLE) {
  1643.       ytitlebox.origin.x = bbox.origin.x;
  1644.       ytitlebox.origin.y = bbox.origin.y;
  1645.     }
  1646.     else if (zooming == MOVEMAINTITLE) {
  1647.       maintitlebox.origin.x = bbox.origin.x;
  1648.       maintitlebox.origin.y = bbox.origin.y;
  1649.     }
  1650.  
  1651.     /*  Call [self display] to give immediate feedback   */
  1652.     [plotParam drawPlotButton:1];
  1653.     [self display];
  1654.     [plotParam drawPlotButton:0];
  1655.   }
  1656.  
  1657.   return self;
  1658. }
  1659.  
  1660. - saveEPS:sender
  1661. {
  1662.   id savePanel = [[SavePanel new] setRequiredFileType:"eps"];
  1663.   char  *eps_fileName;    // Name of the EPS file for output
  1664.  
  1665.   [savePanel setTitle:"Save EPS"]; /* make sure title is OK */
  1666.   if ([savePanel runModal])  {
  1667.     eps_fileName = (char *) calloc(strlen([savePanel filename])+1, sizeof(char));
  1668.     strcpy(eps_fileName, [savePanel filename]);
  1669.     [self savePSCode:eps_fileName];
  1670.   }
  1671.  
  1672.   return self;
  1673. }
  1674.  
  1675. - savePSCode:(char *)aFile
  1676. {
  1677.     NXStream    *psStream;
  1678.  
  1679.     psStream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  1680.     if (!psStream)  {
  1681.       return self;
  1682.     }
  1683.     [self getBounds:&bounds];
  1684.     [self copyPSCodeInside:&bounds to:psStream];
  1685.        
  1686.     NXFlush(psStream);
  1687.     NXSaveToFile(psStream, aFile);
  1688.     NXCloseMemory(psStream, NX_FREEBUFFER);
  1689.  
  1690.     return self;
  1691. }
  1692.  
  1693. /*
  1694.  * Following code taken from the Graph example in NextDeveloper/Examples.
  1695.  *
  1696.  * Copies the current view into the Pasteboard as PostScript.
  1697.  */
  1698. - copyPScode:sender
  1699. {
  1700.     NXStream *psStream;
  1701.     id        pb;
  1702.     char     *data;
  1703.     int       dataLen, maxDataLen;
  1704.  
  1705.   /* Open a stream on memory where we will collect the PostScript */
  1706.     psStream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  1707.     if (!psStream)
  1708.       return self;
  1709.  
  1710.   /* Tell the Pasteboard we're going to copy PostScript */
  1711.     pb = [Pasteboard new];
  1712.     [pb declareTypes:&NXPostScriptPboardType num:1 owner:self];
  1713.  
  1714.   /* writes the PostScript for the whole plot as EPS into the stream */
  1715.     [self getBounds:&bounds];
  1716.     [self copyPSCodeInside:&bounds to:psStream];
  1717.  
  1718.   /* get the buffered up PostScript out of the stream */
  1719.     NXGetMemoryBuffer(psStream, &data, &dataLen, &maxDataLen);
  1720.  
  1721.   /* put the buffer in the Pasteboard, free the stream (and the buffer) */
  1722.     [pb writeType:NXPostScriptPboardType data:data length:dataLen];
  1723.     NXCloseMemory(psStream, NX_FREEBUFFER);
  1724.     return self;
  1725. }
  1726.  
  1727. @end
  1728.